use serde::Serialize;
use std::fmt::Display;
use tracing::error;
use warp::http::StatusCode;
use warp::reply;
use warp::reply::WithStatus;

pub trait ToStatusCode {
    fn status_code(&self) -> StatusCode;
}

pub trait IntoWithStatus {
    fn into_with_status(self) -> WithStatus<String>;
}

impl<T: Serialize, E: Display + ToStatusCode> IntoWithStatus for Result<T, E> {
    fn into_with_status(self) -> WithStatus<String> {
        match self {
            Ok(t) => match serde_json::to_string(&t) {
                Ok(s) => reply::with_status(s, StatusCode::OK),
                Err(e) => {
                    error!(%e, "Serialize response");
                    reply::with_status(
                        format!("Failed to serialize: {}", e),
                        StatusCode::INTERNAL_SERVER_ERROR,
                    )
                }
            },
            Err(e) => reply::with_status(e.to_string(), e.status_code()),
        }
    }
}

pub trait IntoWithStatusForUnit {
    fn into_with_status(self) -> WithStatus<String>;
}

impl<E: Display + ToStatusCode> IntoWithStatusForUnit for Result<(), E> {
    fn into_with_status(self) -> WithStatus<String> {
        match self {
            Ok(_) => reply::with_status("".to_string(), StatusCode::OK),
            Err(e) => reply::with_status(e.to_string(), e.status_code()),
        }
    }
}
